import {TileSource} from "./TileSource";
import {LatLng, LatLngBounds} from "../../";

export class OSMTileSource extends TileSource {
    public static OSM_URL_A = "http://a.tile.openstreetmap.org/{z}/{x}/{y}.png";
    public static OSM_URL_B = "http://b.tile.openstreetmap.org/{z}/{x}/{y}.png";
    public static OSM_URL_C = "http://c.tile.openstreetmap.org/{z}/{x}/{y}.png";

    queryViewBounds(bounds: LatLngBounds, zoom: number): number[] {
        let startX = Math.min(Math.max(this.lon2tile(bounds.southWest.longitude, zoom), 0)
            , Math.pow(2, zoom) - 1);
        let endX = Math.min(Math.max(this.lon2tile(bounds.northEast.longitude, zoom), 0)
            , Math.pow(2, zoom) - 1);
        let endY = Math.min(Math.max(this.lat2tile(bounds.southWest.latitude, zoom), 0)
            , Math.pow(2, zoom) - 1);
        let startY = Math.min(Math.max(this.lat2tile(bounds.northEast.latitude, zoom), 0)
            , Math.pow(2, zoom) - 1);

        return [startX, endX, startY, endY];
    }

    getBounds(x: number, y: number, z: number): LatLngBounds {
        let bbox = this.map.crs.bbox;

        let north = this.tile2lat(y, z);
        let south = this.tile2lat(y + 1, z);
        let west = this.tile2lon(x, z);
        let east = this.tile2lon(x + 1, z);

        return new LatLngBounds(new LatLng(south, west),
            new LatLng(north, east));
    }

    getURL(x: number, y: number, z: number): string {
        return OSMTileSource.OSM_URL_B.replace("{z}", String(z))
            .replace("{y}", String(y))
            .replace("{x}", String(x));
    }

    protected lon2tile(lon: number, zoom: number) {
        return (Math.floor((lon + 180) / 360 * Math.pow(2, zoom)));
    }

    protected lat2tile(lat: number, zoom: number) {
        return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180)
            + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom)));
    }

    protected tile2lon(x: number, z: number) {
        return (x / Math.pow(2, z) * 360 - 180);
    }

    protected tile2lat(y: number, z: number) {
        let n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
        return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));
    }
}
